package com.myselia.stem.communication.states; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.websocketx.WebSocket13FrameDecoder; import io.netty.handler.codec.http.websocketx.WebSocket13FrameEncoder; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.Map.Entry; import com.myselia.javacommon.communication.codecs.StringToTransmissionDecoder; import com.myselia.javacommon.communication.codecs.TransmissionToWebSocketEncoder; import com.myselia.javacommon.communication.codecs.WebSocketDecoder; import com.myselia.javacommon.communication.units.Transmission; import com.myselia.javacommon.communication.units.TransmissionBuilder; import com.myselia.javacommon.constants.opcode.ActionType; import com.myselia.javacommon.constants.opcode.ComponentType; import com.myselia.javacommon.constants.opcode.OpcodeBroker; import com.myselia.javacommon.constants.opcode.operations.LensOperation; import com.myselia.javacommon.constants.opcode.operations.StemOperation; import com.myselia.javacommon.framework.communication.WebSocketHelper; import com.myselia.stem.communication.CommunicationDock; import com.myselia.stem.communication.StemClientSession; import com.myselia.stem.communication.handlers.ComponentHandlerBase; import com.myselia.stem.communication.handlers.ComponentHandlerFactory; public class HttpHandshakeConnectionState implements ConnectionState { //TODO: Perhaps implement netty.WebSocketServerHandshaker and WebSocketServerHandshakerFactory private final String keyStringSearch = "Sec-WebSocket-Key: "; private ComponentHandlerBase handler; private StemClientSession session; private String webSocketKey = null; boolean connectionEstablished = false; @Override public void primeConnectionState(StemClientSession session) { this.session = session; } @Override @SuppressWarnings("unchecked") public void process(Transmission s) throws IOException { if (connectionEstablished) { System.err.println("CONNECTION ESTABLISHED"); // Wait for the setup packet after the headers have been handled handleSetupPacket(s); } else { // This is first contact with the WebSocket client, generate an // accept response setWebSocketKey((ArrayList<String>) session.getFirstContact()); handleHeaders(session.getClientChannel()); //Setup the pipeline since the WebSocket handshake has been completed setupPipeline(session.getClientChannel()); connectionEstablished = true; } } private void setWebSocketKey(ArrayList<String> HTTPRequest) { Iterator<String> it = HTTPRequest.iterator(); while (it.hasNext()) { String s = it.next(); if (s.contains(keyStringSearch)) { webSocketKey = s.substring(keyStringSearch.length()); break; } } } @Override public ComponentHandlerBase getHandler() { return handler; } @Override public void setHandler(ComponentHandlerBase handler) { this.handler = handler; } private void handleHeaders(Channel ch) { WebSocketHelper.sendHandshakeResponse(ch, webSocketKey); } private void handleSetupPacket(Transmission s) throws IOException { ComponentHandlerBase handler = null; try { handler = ComponentHandlerFactory.createHandler(s, session); System.out.println("VALUE OF READY IS: " + handler.ready()); if (handler.ready()) { this.setHandler(handler); session.setComponentHandler(handler); session.setConnectionState(session.getStateContainer().getConnectedState()); session.getClientChannel().writeAndFlush(connectionReadyPacket(true)); } else { // Tell the session thread to die session.getClientChannel().writeAndFlush(connectionReadyPacket(false)); session.die(); } } catch (Exception e) { System.err.println("Setup packet from component is malformed!"); e.printStackTrace(); } } /** * This method sets up the channel pipeline to contain the proper codecs for websocket payload transportation * */ private void setupPipeline(Channel ch) { ChannelPipeline pipeline = ch.pipeline(); //Decoders pipeline.remove("frameDecoder"); pipeline.replace("stringDecoder", "webSocketFrameDecoder", new WebSocket13FrameDecoder(true, false, 4096)); pipeline.addAfter("webSocketFrameDecoder", "webSocketDecoder", new WebSocketDecoder()); pipeline.addAfter("webSocketDecoder", "transmissionDecoder", new StringToTransmissionDecoder()); //Encoders pipeline.replace("stringEncoder", "transmissionEncoder", new TransmissionToWebSocketEncoder()); pipeline.addFirst("webSocketFrameEncoder", new WebSocket13FrameEncoder(false)); System.err.println("[HTTP Handshaker] ~~ Pipeline Setup Complete"); Iterator<Entry<String, ChannelHandler>> it = pipeline.iterator(); while (it.hasNext()) { Entry<String, ChannelHandler> s = it.next(); System.out.println("Entry is: " + s.getKey() ); System.out.println("\t->Handler: " + s.toString() ); } } private Transmission connectionReadyPacket(boolean ok) { String isReady = null; LensOperation setupStatus; if (ok) { isReady = "true"; setupStatus = LensOperation.SETUPOK; } else { isReady = "false"; setupStatus = LensOperation.SETUPERR; } TransmissionBuilder tb = new TransmissionBuilder(); String from = OpcodeBroker.make(ComponentType.STEM, CommunicationDock.stemCertificate.getUUID(), ActionType.DATA, StemOperation.SETUP); String to = OpcodeBroker.make(ComponentType.LENS, this.handler.getCertificate().getUUID(), ActionType.DATA, setupStatus); tb.newTransmission(from, to); tb.addAtom("ready", "boolean", isReady); Transmission t = tb.getTransmission(); return t; } public String toString() { return "[Connection State] ~~ HTTP Handshaker"; } }